﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.ComponentModel;

namespace ViewMaker.Core.Controls
{
    /// <summary>
    /// グリッドViewコントロール
    /// </summary>
    public class GridViewControl : PanelViewControl
    {
        public new class Properties : PanelViewControl.Properties
        {
            public const string ItemColumnSize = "ItemColumnSize";
            public const string ItemRowSize = "ItemRowSize";
            public const string Orientation = "Orientation";
        }

        /// <summary>
        /// Gridの列数
        /// </summary>
        [Display(Name = "Grid ColumnSize", GroupName = LayoutGroup)]
        [DefaultValue(1)]
        public int ItemColumnSize { get; set; }

        /// <summary>
        /// Gridの行数
        /// </summary>
        [Display(Name = "Grid RowSize", GroupName = LayoutGroup)]
        [DefaultValue(1)]
        public int ItemRowSize { get; set; }

        /// <summary>
        /// アイテムを配置する方向
        /// </summary>
        [Display(GroupName = LayoutGroup)]
        [View(ViewControlType.ComboBox)]
        [ViewProperty(ComboBoxViewControl.Properties.ItemsSource, "OrientationList")]
        public LayoutOrientation Orientation { get; set; }

        #region ShouldSerialize

        public override bool ShouldSerializeHeaderHorizontalContentAlignment() { return true; }
        public override bool ShouldSerializeVerticalAlignment() { return true; }

        #endregion

        public GridViewControl()
        {
            ControlType = ViewControlType.Grid;
            IsPanel = true;
            HeaderHorizontalContentAlignment = ViewMakerSettings.Instance.DefaultGridHeaderHorizontalContentAlignment;
            ItemColumnSize = 1;
            ItemRowSize = 1;
            Orientation = ViewMakerSettings.Instance.DefaultGridOrientation;
            HeaderPosition = ViewMakerSettings.Instance.DefaultGridHeaderPosition;
        }

        private List<Cell> cells;
        private int columnIdx;
        private int rowIdx;
        private int columnSize;
        private int rowSize;

        private Dictionary<int, string> rowHeight;
        private Dictionary<int, string> columnWidth;

        public override List<XamlNode> CreateXaml(XamlNode parent)
        {
            rowHeight = new Dictionary<int, string>();
            columnWidth = new Dictionary<int, string>();
            cells = new List<Cell>();
            columnIdx = 0;
            rowIdx = 0;

            SetupRowColumnSize();

            XamlNode panel = CreatePanel(parent, "Grid");
            foreach (var child in Item.Children)
            {
                var headerItem = child.Element as HeaderViewControl;
                int itemIdx = 0;

                if (headerItem != null)
                {
                    foreach (var item in CreateHeaderControl(child, panel))
                    {
                        panel.AppendChild(item);
                        SetGridRowColumn(child, item, itemIdx, itemIdx == 0 && headerItem.ActualHeaderPosition == LayoutHeaderPosition.Flat);
                        itemIdx++;
                    }
                }
                else
                {
                    foreach (var item in child.CreateXaml(panel))
                    {
                        SetItemAttribute(child, item);
                        panel.AppendChild(item);
                        SetGridRowColumn(child, item, itemIdx, false);
                        itemIdx++;
                    }
                }
                SetGridWidthHeight(child);

                GetNextCell(child);
            }
            CreateRowDefinition(panel);
            CreateColumnDefinition(panel);
            SetBindingAttribute(panel, "DataContext", Item.BindPath, true);
            ApplyBaseProperty(panel);
            var list = new List<XamlNode>();
            list.Add(Decoration ?? panel);
            return list;
        }

        protected override void SetItemAttribute(ViewLayoutItem targetItem, XamlNode targetXaml)
        {
            base.SetItemAttribute(targetItem, targetXaml);
            if (!targetItem.Control.VerticalAlignment.HasValue) targetXaml.SetAttribute("VerticalAlignment", LayoutVerticalAlignment.Center.ToString());
        }


        /// <summary>
        /// Grid全体のRowとColumnサイズの配置用の暫定値をもとめる
        /// </summary>
        private void SetupRowColumnSize()
        {
            rowSize = ItemRowSize;
            columnSize = ItemColumnSize;
            if (columnSize != 1 && rowSize == 1)
            {
                rowSize = (Item.Children.Sum(x => x.Control.RowSpan == 0 ? columnSize : x.Control.RowSpan) - 1) / columnSize + 1;
            }
            else if (rowSize != 1 && columnSize == 1)
            {
                columnSize = (Item.Children.Sum(x => x.Control.ColumnSpan == 0 ? rowSize : x.Control.ColumnSpan) - 1) / rowSize + 1;
            }
            else if (rowSize == 1 && columnSize == 1)
            {
                foreach (var child in this.Item.Children)
                {
                    var header = child.Element as HeaderViewControl;
                    if (header != null) if (header.ActualHeaderPosition == LayoutHeaderPosition.Flat) rowSize = 2;
                }
            }
        }

        private void SetGridWidthHeight(ViewLayoutItem child)
        {
            var headerItem = child.Element as HeaderViewControl;
            if (!string.IsNullOrEmpty(child.Control.GridHeight)) rowHeight[rowIdx] = child.Control.GridHeight;
            if (headerItem != null && headerItem.ActualHeaderPosition == LayoutHeaderPosition.Flat)
            {
                //ヘッダーはauto前提
                if (!columnWidth.ContainsKey(columnIdx)) columnWidth[columnIdx] = "auto";
                if (!string.IsNullOrEmpty(child.Control.GridWidth)) columnWidth[columnIdx + 1] = child.Control.GridWidth;
            }
            else
            {
                if (!string.IsNullOrEmpty(child.Control.GridWidth)) columnWidth[columnIdx] = child.Control.GridWidth;
            }
        }

        private void CreateColumnDefinition(XamlNode panel)
        {
            if (cells.Count == 0) return;
            var columns = panel.AddElement("Grid.ColumnDefinitions");
            for (int ix = 0; ix <= cells.Max(x=>x.Column); ix++)
            {
                var col = columns.AddElement("ColumnDefinition");
                if (columnWidth.ContainsKey(ix)) col.SetAttribute("Width",  columnWidth[ix]);
            }

        }

        private void CreateRowDefinition(XamlNode panel)
        {
            if (cells.Count == 0) return;
            var rows = panel.AddElement("Grid.RowDefinitions");
            for (int iy = 0; iy <= cells.Max(x=>x.Row); iy++)
            {
                var rowItem = rows.AddElement("RowDefinition");
                rowItem.SetAttribute("Height", rowHeight.ContainsKey(iy) ? rowHeight[iy] : "auto");
            }
        }

        private void GetNextCell(ViewLayoutItem child)
        {
            int columnUseSize = GetColumnUseSize(child);
            for (int ix = 0; ix < columnUseSize; ix++)
            {
                for (int iy = 0; iy < child.Control.RowSpan; iy++)
                {
                    var cell = new Cell { Column = columnIdx + ix, Row = rowIdx + iy };
                    if (!cells.Any(x=>x.Column==cell.Column && x.Row == cell.Row)) cells.Add(cell);
                }
            }
            do
            {
                if (Orientation == LayoutOrientation.Vertical)
                {
                    rowIdx++;
                    if (rowIdx >= rowSize)
                    {
                        columnIdx++;
                        rowIdx -= rowSize;
                    }
                }
                else
                {
                    columnIdx++;
                    if (columnIdx >= columnSize)
                    {
                        rowIdx++;
                        columnIdx -= columnSize;
                    }
                }
            } while (cells.Any(x=>x.Column==columnIdx && x.Row == rowIdx));
            //Debug.WriteLine("Found space " + columnIdx.ToString() + " " + rowIdx.ToString());
        }

        private int GetColumnUseSize(ViewLayoutItem item)
        {
            int columnSpan = item.Control.ColumnSpan;
            if (columnSpan == 0) return GetCurrentColumnSize() - columnIdx;
            var headerControl = item.Element as HeaderViewControl;
            if (headerControl != null)
            {
                if (headerControl.ActualHeaderPosition == LayoutHeaderPosition.Flat) columnSpan += 1;
            }
            return columnSpan;
        }

        private void SetGridRowColumn(ViewLayoutItem child, XamlNode item, int itemIdx, bool isHeader)
        {
            SetGridRowColumn(item,rowIdx,columnIdx + itemIdx);
            if (!isHeader)
            {
                var headerControl = child.Element as HeaderViewControl;
                int? columnSpan = null;
                if (child.Control.ColumnSpan > 1) columnSpan = child.Control.ColumnSpan;
                if (child.Control.ColumnSpan == 0) columnSpan = GetCurrentColumnSize() - columnIdx;
                if (columnSpan.HasValue && columnSpan.Value > 1) item.SetAttribute("Grid.ColumnSpan", columnSpan.Value.ToString());

                int? rowSpan = null;
                if (child.Control.RowSpan > 1) rowSpan = child.Control.RowSpan;
                if (child.Control.RowSpan == 0) rowSpan = GetCurrentRowSize() - rowIdx;
                if (rowSpan.HasValue && rowSpan.Value > 1) item.SetAttribute("Grid.RowSpan", rowSpan.Value.ToString());
            }
        }

        private int GetCurrentColumnSize()
        {
            int columnMaxSize = 0;
            if (cells.Count > 0) columnMaxSize = cells.Max(x => x.Column) + 1;
            if (columnMaxSize < columnSize) columnMaxSize = columnSize;
            return columnMaxSize;
        }

        private int GetCurrentRowSize()
        {
            int rowMaxSize = 0;
            if (cells.Count > 0) rowMaxSize = cells.Max(x => x.Row) + 1;
            if (rowMaxSize < rowSize) rowMaxSize = rowSize;
            return rowMaxSize;
        }

        private void SetGridRowColumn(XamlNode item, int row, int column)
        {
            item.SetAttribute("Grid.Row", row.ToString());
            item.SetAttribute("Grid.Column", column.ToString());
        }

        internal struct Cell
        {
            public int Column { get; set; }
            public int Row { get; set; }

        }
    }

}
